
function isObject(value) {
    return typeof value === 'object' && value
}
function _isNaN(value) {
    return Number.isNaN(value)
}
class miniVue{
    constructor(options = {}) {
        // 传入配置对象
        this.$options = options
        // 根元素
        this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
        this.$data = options.data
        this.$methods = options.methods
        // proxy代理
        this.proxy(this.$data)
        // 实例化Observe
        new Observe(this.$data)
        new Compiler(this)
    }
    // 数据代理
    proxy(data) {
        // 只代理对象
        if (!isObject(data) || _isNaN(data)) return;
        // 代理数据
        Object.keys(data).forEach(key => {
            Object.defineProperty(this, key,{
                enumerable: true,
                configurable: true,
                // get,set
                get() {
                    return data[key]
                },
                set(newValue) {
                    if (newValue === data[key] || _isNaN(data[key])) return;
                    data[key] = newValue
                } 
            })
        })
    }
}
// 管理依赖
class Dep{
    constructor() {
        this.deps = new Set()
    }
    // 添加依赖
    add(dep) {
        if(dep && dep.update) this.deps.add(dep)
    }
    // 通知更新
    notify() {
        this.deps.forEach(dep => dep.update && dep.update())
    }
}
// 响应式,不考虑数组
class Observe{
    constructor(data) {
        this.walk(data)
    }
    walk(data) {
        // 拿到data
       
        if (!isObject(data))return;
        Object.keys(data).forEach(key => this.defineReactive(data, key, data[key]))
        
    }
    defineReactive(data, key, value) {
        // 实例化依赖
        const vm = this
        let dep = new Dep()
        // 递归
        if (isObject(value))
            this.walk(value)
        Object.defineProperty(data, key,{
            enumerable: true,
            configurable: true,
            // 收集依赖
            get() {
                Dep.target && dep.add(Dep.target);
                return value  
            },
            set(newValue) {
                if (newValue === value || _isNaN(newValue) && _isNaN(value)) return;
                value = newValue
                // 通知更新
                dep.notify()
            }
        })
    }
}
// 观察类
class Watcher{
    constructor(vm, key, cb) {
        this.vm = vm
        this.key = key
        this.cb = cb
        Dep.target = this
        // 存储当前旧值
        this.__old = vm[key]
        Dep.target = null
    }
    update() {
        // 拿到data
        const value = this.vm[this.key]
        if (this.__old === value || _isNaN(this.__old) && __isNaN(value)) return;
        this.cb(value)
        // 重置旧值
        this.__old = value

    }
}
// 编译类
class Compiler{
    constructor(vm) {
        this.vm = vm
        this.methods = vm.$methods
        // 子节点编译
        this.compile(vm.$el)
    }
    compile(el) {
        let childNodes = el.childNodes
        Array.from(childNodes).forEach(node => {
            // 如果是文本节点
            if (this.isTextNode(node)) {
                // console.log(node)
                this.compileText(node)
            }
            // 如果是元素节点
            else if (this.isElementNode(node)) {
                this.compileElement(node)
            }
            // 判断子节点是否还有子节点
            if (node.childNodes && node.childNodes.length)
                this.compile(node)
        }) 
    }
    compileElement(node) { 
        const attributes = node.attributes
        // 判断是否有指令
        if (attributes.length) {
            Array.from(attributes).forEach(attributes => {
                const item = attributes.name
                // console.log(typeof item)
                if (this.isDirective(item)) {
                    const attrName = item.indexOf(':') > -1 ? item.slice(5) : item.slice(2)
                    const key = attributes.value
                    this.update(node,attrName,key)
                }
            })
        }
    }
    // {{count}} => 映射相互结果
    compileText(node) { 
        const reg = /\{\{(.+?)\}\}/
        const value = node.textContent
        // console.log(value)
        if (reg.test(value)) {
            const key = RegExp.$1.trim()
            node.textContent = this.vm[key]
            // 实例化观察者
            new Watcher(this.vm, key, newVal => {
                node.textContent = newVal
            })
        }

    }
    isElementNode(node) {
        return node.nodeType === 1
     }
    isTextNode(node) {
        return node.nodeType === 3
    }
    isDirective(dir) {
        // v-
        return dir.startsWith('v-')
    }
    // 判断指令，并更新，绑定事件
    update(node, attrName, key) {
        // v-text
        if (attrName === 'text') {
            node.textContent = this.vm[key]
            new Watcher(this.vm,key, newVal => {
                node.textContent = newVal
            })
        }
        // v-model
        else if (attrName === 'model') {
            node.value = this.vm[key]
            new Watcher(this.vm, key, newVal => {
                node.value = this.vm[key]
            })
            node.addEventListener('input', () => {
                this.vm[key] = node.value
                node.value = this.vm[key]
            })
        }
        // v-click
        else if (attrName === 'click') {
            node.addEventListener(attrName,this.methods[key].bind(this.vm))
        }

      
    }
}